﻿#include"XQLog.hpp"
#include"XQTaskPool.h"
#include"XQFuncEvent.h"
#include"XQFuncEventFilter.hpp"
#include<iostream>
#include<QCoreApplication>
#include<QDateTime>
#include<QJsonObject>
#include<QJsonArray>
#include<QJsonDocument>
#include<QThread>
#include<QFile>
#include<QFileInfo>
#include<QDir>
#include<QDebug>
XQThreadQueue<XQLogOut> XQLog::m_queue;
std::atomic<bool> XQLog::m_taskSleep = false;
QWaitCondition XQLog::m_wait;
QReadWriteLock XQLog::m_taskLock;
QReadWriteLock XQLog::m_lock;
QHash<QString, XQLogSettings> XQLog::m_Settings = { 
	{"XQInfo",{new XQLog("XQInfo",true),LogType::Info,XQLog::cmd_out | XQLog::cmd_cpp | XQLog::showLogType}} ,
	{"XQDebug",{new XQLog("XQDebug",true),LogType::Debug,XQLog::cmd_out | XQLog::cmd_cpp | XQLog::showLogType}} ,
	{"XQWarning",{new XQLog("XQWarning",true),LogType::Warning,XQLog::cmd_out | XQLog::cmd_cpp | XQLog::showLogType}} ,
	{"XQCritical",{new XQLog("XQCritical",true),LogType::Critical,XQLog::cmd_out | XQLog::cmd_cpp | XQLog::showLogType}} ,
	{"XQFatal",{new XQLog("XQFatal",true),LogType::Fatal,XQLog::cmd_out | XQLog::cmd_cpp | XQLog::showLogType}}
};
QThread* XQLog::m_task = nullptr;
XQLog::XQLog(const QString& key)
	:m_key(key)
{
	init();
}
XQLog::XQLog(const QString& key, bool isGlobal)
	:m_key(key)
{
	if(isGlobal)
	{
		//QObject::connect(qApp, &QCoreApplication::aboutToQuit, this, &XQLog::deleteLater);
		connect(this, &QObject::destroyed, this, [=]
			{
				QWriteLocker lock(&m_lock);
				m_Settings.remove(m_key);
			});
		installEventFilter(new XQFuncEventFilter(this));
	}
}
XQLog::~XQLog()
{
	out();
}
void XQLog::out()
{
	auto log = Global();
	if (log == nullptr || this == log ||m_data.isEmpty())
		return;//不输出
	QReadLocker lock(&m_lock);
	auto& Settings = m_Settings[m_key];//获得设置信息

	if(log)
	{
		XQLogDate data(m_key, logType(), m_data, data_toString());
		new XQFuncEvent(log, [=]
			{
				emit log->LogOut(data);
			});
	}
	
	if(logShowFlag(cmd_out)&& Settings.CmdOutFunc)
		Settings.CmdOutFunc(this);
	if (logShowFlag(local_out) && Settings.LocalOutFunc)
		Settings.LocalOutFunc(this);
}

XQLog* XQLog::Create(const QString& key, int flags)
{
	if (isDefaultGlobal(key))
		return Global(key);
	auto log = Global(key);
	if (log ==nullptr)
	{
		QWriteLocker lock(&m_lock);
		auto ptr = new XQLog(key, true);
		m_Settings[key].Global = ptr;
		m_Settings[key].flags = flags;
		return m_Settings[key].Global;
	}
	return log;
}

XQLog* XQLog::Create(const QString& key, LogType logType, int flags)
{
	auto log=Create(key, flags);
	if (log)
		log->setLogType(logType);
	return log;
}

XQLog* XQLog::Global(const QString& key)
{
	QReadLocker lock(&m_lock);
	if(m_Settings.contains(key))
		return m_Settings[key].Global;
	return nullptr;
}

XQLog* XQLog::Global()
{
	return Global(m_key);
}

QString XQLog::key() const
{
	return m_key;
}

bool XQLog::isEmpty() const
{
	return m_data.isEmpty();
}

qint64 XQLog::size()const
{
	return m_data.size();
}

XQLogSettings XQLog::settings()
{
	if (Global() == nullptr)
		return XQLogSettings();
	QReadLocker lock(&m_lock);
	return m_Settings[m_key];//获得设置信息
}

//转string显示
QString XQLog::data_toString()
{
	if (Global() == nullptr || m_data.isEmpty())
		return QString();
	QReadLocker lock(&m_lock);
	auto& Settings = m_Settings[m_key];//获得设置信息
	QString text;

	//添加数据
	int count = 0;
	for (auto& data : m_data)
	{
		if (++count != 1)
			text += " ";//2开始添加前缀
		text += variant_to_string(data);
	}
	return std::move(text);
}
//保存成json格式
void LogDefaultLocalOut(XQLog* log)
{
	if (log->localPath().isEmpty())
		return;//目录不存在
	QJsonObject json;
	json["Key"] = log->key();
	json["LogType"] = int(log->logType());
	json["Time"] = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
	QJsonArray arr;
	for (auto& data : log->m_data)
	{
		arr.append(XQLog::variant_to_json(data));
	}
	json["Data"] = arr;
	json["Info"] = log->data_toString();
	XQLog::m_queue.push(XQLogOut(LogOutType::Local, log->logType(), QJsonDocument(json).toJson(), log->localPath()));
	XQLog::m_wait.notify_one();
}

//json转结构体
void XQLogDate::setData(const QVariantMap& map)
{
	key = map["Key"].toString();
	logType = LogType(map["LogType"].toInt());
	time = QDateTime::fromString(map["Time"].toString(), "yyyy-MM-dd hh:mm:ss");
	for (auto json : map["Data"].toJsonArray())
	{
		data.append(std::move(XQLog::json_to_variant(json.toObject())));
	}
	info = map["Info"].toString();
}

XQLogLocalDateList XQLog::read_local()
{
	return std::move(read_local(localPath()));
}

const QVariantList& XQLog::data() const
{
	return m_data;
}

QVariant& XQLog::operator[](int nSel)
{
	return m_data[nSel];
}
int XQLog::logShowFlags()
{
	if (Global() == nullptr)
		return 0;
	QReadLocker lock(&m_lock);
	auto& Settings = m_Settings[m_key];//获得设置信息
	return Settings.flags;
}
bool XQLog::logShowFlags(int flags)
{
	if (Global() == nullptr)
		return 0;
	QReadLocker lock(&m_lock);
	auto& Settings = m_Settings[m_key];//获得设置信息
	return Settings.flags& flags;
}
bool XQLog::logShowFlag(ShowMode mode)
{
	if (Global() == nullptr)
		return 0;
	QReadLocker lock(&m_lock);
	auto& Settings = m_Settings[m_key];//获得设置信息
	return Settings.flags& mode;
}
LogType XQLog::logType()
{
	if (Global() == nullptr)
		return LogType::null;
	QReadLocker lock(&m_lock);
	auto& Settings = m_Settings[m_key];//获得设置信息
	return Settings.logType;
}
LogSaveType XQLog::saveLocalType()
{
	if (Global() == nullptr)
		return LogSaveType::null;
	QReadLocker lock(&m_lock);
	auto& Settings = m_Settings[m_key];//获得设置信息
	return Settings.saveLocalType;
}
QString XQLog::localPath()
{
	if (Global() == nullptr)
		return QString();
	QReadLocker lock(&m_lock);
	auto& Settings = m_Settings[m_key];//获得设置信息
	return Settings.localPath;
}
QString XQLog::timeFormat()
{
	if (Global() == nullptr)
		return QString();
	QReadLocker lock(&m_lock);
	auto& Settings = m_Settings[m_key];//获得设置信息
	return Settings.timeFormat;
}
void XQLog::setLogShowFlags(int flags)
{
	if (Global() == nullptr)
		return;
	if ((flags & cmd_qt) && (flags & cmd_cpp))
	{//两个都有有问题这两项保持原样
		flags &= (~cmd_qt); flags &= (~cmd_cpp);
	}
	QWriteLocker lock(&m_lock);
	auto& Settings = m_Settings[m_key];//获得设置信息
	Settings.flags = flags;
}
void XQLog::setLogShowFlag(ShowMode mode, bool on)
{
	if (Global() == nullptr)
		return;
	QWriteLocker lock(&m_lock);
	auto& Settings = m_Settings[m_key];//获得设置信息
	if(on)
		Settings.flags|=mode;
	else
		Settings.flags&=(~mode);
	if(mode== cmd_cpp)//cpp和qt互斥
		Settings.flags &= (~cmd_qt);
	else if (mode == cmd_qt)
		Settings.flags &= (~cmd_cpp);
}
void XQLog::setLogType(LogType type)
{
	if (Global() == nullptr)
		return;
	QWriteLocker lock(&m_lock);
	auto& Settings = m_Settings[m_key];//获得设置信息
	Settings.logType = type;
}
void XQLog::saveLocalType(LogSaveType type)
{
	if (Global() == nullptr)
		return;
	QWriteLocker lock(&m_lock);
	auto& Settings = m_Settings[m_key];//获得设置信息
	Settings.saveLocalType = type;
}
void XQLog::setLocalPath(const QString& path)
{
	if (Global() == nullptr)
	{
		XQFatal << "单例模式不存在";
		return;
	}
	QWriteLocker lock(&m_lock);
	auto& Settings = m_Settings[m_key];//获得设置信息
	Settings.localPath = path;
}
void XQLog::setTimeFormat(const QString& format)
{
	if (Global() == nullptr)
		return;
	QWriteLocker lock(&m_lock);
	auto& Settings = m_Settings[m_key];//获得设置信息
	Settings.timeFormat = format;
}
void XQLog::setLogOutFunc_CMD(std::function<void(XQLog*)> outFunc)
{
	if (Global() == nullptr)
		return;
	QWriteLocker lock(&m_lock);
	auto& Settings = m_Settings[m_key];//获得设置信息
	Settings.CmdOutFunc = outFunc;
}
void XQLog::setLogOutFunc_Local(std::function<void(XQLog*)> outFunc)
{
	if (Global() == nullptr)
		return;
	QWriteLocker lock(&m_lock);
	auto& Settings = m_Settings[m_key];//获得设置信息
	Settings.LocalOutFunc = outFunc;
}
void XQLog::clear_local_file()
{
	auto path = localPath();
	QFile::remove(path);
}
void XQLog::push_back(QVariantList& list)
{
	m_data.append(list);
}
void XQLog::push_back(const QVariantList& list)
{
	m_data.append(list);
}
void XQLog::push_back(QVariantList&& list)
{
	m_data.append(std::move(list));
}
void XQLog::push_back(const XQLogLocalDateList& list)
{
	Push_Back(list);
}
void XQLog::push_back(XQLogLocalDateList& list)
{
	Push_Back(list);
}
void XQLog::push_back(XQLogLocalDateList&& list)
{
	Push_Back(list);
}
XQLog& XQLog::operator<<(QVariantList& list)
{
	push_back(list);
	return *this;
}
XQLog& XQLog::operator<<(const QVariantList& list)
{
	push_back(list);
	return *this;
}
XQLog& XQLog::operator<<(QVariantList&& list)
{
	push_back(std::move(list));
	return *this;
}
XQLog& XQLog::operator<<(XQLogDate&& data)
{
	Push_Back(data);
	return *this;
}
XQLog& XQLog::operator<<(XQLogDate& data)
{
	Push_Back(data);
	return *this;
}
XQLog& XQLog::operator<<(const XQLogDate& data)
{
	Push_Back(data);
	return *this;
}
XQLog& XQLog::operator<<(XQLogLocalDateList&& list)
{
	Push_Back(list);
	return *this;
}
XQLog& XQLog::operator<<(XQLogLocalDateList& list)
{
	Push_Back(list);
	return *this;
}
XQLog& XQLog::operator<<(const XQLogLocalDateList& list)
{
	Push_Back(list);
	return *this;
}
bool XQLog::isDefaultGlobal(const QString& key)
{
	return QStringList{ "Info" ,"Debug","Warning" ,"Critical" ,"Fatal" }.contains(key);
}

void XQLog::remove(const QString& key)
{
	auto log = Global(key);
	if (log == nullptr)
		return;
	QWriteLocker lock(&m_lock);
	m_Settings.remove(key);
	log->deleteLater();
}

QString XQLog::logType_toString(LogType logType)
{
	switch (logType)
	{
	case LogType::null:
		break;
	case LogType::Info:return "Info";
		break;
	case LogType::Debug:return "Debug";
		break;
	case LogType::Warning:return "Warning";
		break;
	case LogType::Critical:return "Critical";
		break;
	case LogType::Fatal:return "Fatal";
		break;
	default:
		break;
	}
	return QString();
}

bool XQLog::isTaskSleep()
{
	return m_taskSleep;
}

void XQLog::wait_task()
{
	while (!m_taskSleep||!m_queue.isEmpty())
	{
		QCoreApplication::processEvents();
	}
}

XQLogLocalDateList XQLog::read_local(const QString& path)
{
	XQLogLocalDateList list;
	if (path.isEmpty() || !QFile(path).exists())
		return std::move(list);
	QReadLocker lcok(&XQLog::m_lock);
	QFile file(path);
	if (!file.open(QIODeviceBase::ReadOnly))
		return std::move(list);//打开失败
	QJsonDocument json = QJsonDocument::fromJson("[" + file.readAll() + "]");
	if (json.isNull())
		return std::move(list);
	auto rows = json.toVariant().toList();
	for (auto& row : rows)
	{
		list << XQLogDate(row.toMap());
	}

	return std::move(list);
}

void XQLog::init()
{
	if(m_task == nullptr&&qApp)
	{
		QWriteLocker lock(&XQLog::m_lock);
		//初始化默认的目录
		auto dir = QDir::currentPath();
		XQLog::m_Settings["XQInfo"].localPath = dir + "/Info.log";
		XQLog::m_Settings["XQDebug"].localPath = dir + "/Debug.log";
		XQLog::m_Settings["XQWarning"].localPath = dir + "/Warning.log";
		XQLog::m_Settings["XQCritical"].localPath = dir + "/Critical.log";
		XQLog::m_Settings["XQFatal"].localPath = dir + "/Fatal.log";
		//创建线程输出
		m_task= QThread::create([]
			{
				auto& queue = XQLog::m_queue;
				while (!XQLog::m_task->isInterruptionRequested())
				{
					//XQInfo << "线程启动了";
					if (queue.isEmpty())//如果空的话就休眠此线程
					{
						QReadLocker lock(&m_taskLock);
						XQLog::m_taskSleep = true;
						XQLog::m_wait.wait(&m_taskLock);
						XQLog::m_taskSleep = false;
					}
					if (queue.isEmpty())
						continue;
					auto data = queue.pop();//获取一个
					
					if (data.outType == LogOutType::CMD)
					{
						if (data.flags & XQLog::cmd_cpp)
						{
							LogCMDOut_Cpp(data);
						}
						else if (data.flags & XQLog::cmd_qt)
						{
							LogCMDOut_Qt(data);
						}
					}
					else if (data.outType == LogOutType::Local)
					{
						LogLocalOut(data);
					}
				}
			});
		
		//程序退出时结束线程
		QObject::connect(qApp, &QCoreApplication::aboutToQuit,  [] 
			{//线程退出
				auto& task = XQLog::m_task;
				task->requestInterruption();
				XQLog::m_wait.notify_all();
				task->quit();
				task->wait();
				//清理创建的单例
				QWriteLocker lock(&XQLog::m_lock);
				for (auto&settings: XQLog::m_Settings)
				{
					if (settings.Global)
						settings.Global->deleteLater();
				}
			});
		//启动线程
		m_task->start();
	}
}

void XQLog::Push_Back(const XQLogLocalDateList& list)
{
	for (auto& data : list)
	{
		XQLog(m_key)<<data;
	}
}

void XQLog::Push_Back(const XQLogDate& data)
{
	push_back(data.time, data.info);
}

void LogDefaultCMDOut(XQLog* log)
{
	XQLogOut logOut(LogOutType::CMD, log->settings(), log->data_toString().toUtf8());
	if(log->logShowFlag(XQLog::cmd_queue))
	{
		XQLog::m_queue.push(logOut);
		XQLog::m_wait.notify_one();
	}
	else if (log->logShowFlag(XQLog::cmd_cpp))
	{
		LogCMDOut_Cpp(logOut);
	}
	else if (log->logShowFlag(XQLog::cmd_qt))
	{
		LogCMDOut_Qt(logOut);
	}
	
}

void LogCMDOut_Qt(const XQLogOut& data)
{
	auto out = [=](const QString& text) {
		switch (data.logType)
		{
		case LogType::Info:qInfo() << text;
			break;
		case LogType::Debug:qDebug() << text;
			break;
		case LogType::Warning:qWarning() << text;
			break;
		case LogType::Critical:qCritical() << text;
			break;
		case LogType::Fatal:qFatal(text.toUtf8());
			break;
		default:
			break;
		};
	};
	QString text;
	auto flags = data.flags;
	if (flags & XQLog::showTime)
	{
		text += (QDateTime::currentDateTime().toString(data.timeFormat)+" ");
	}
	if(flags&XQLog::showLogType)
	{
		text += (XQLog::logType_toString(data.logType) + " ");
	}
	if (flags & XQLog::dataLineFeed)
	{
		out(text);
		text.clear();
	}
	if (!text.isEmpty())
		text += " ";
	text += data.data.trimmed();
	out(text);
}

void LogCMDOut_Cpp(const XQLogOut& log)
{
	auto flags = log.flags;
	if (flags & XQLog::showTime)
	{
		std::cout<<(QDateTime::currentDateTime().toString(log.timeFormat) + " ").toLocal8Bit().data();
	}
	if (flags & XQLog::showLogType)
	{
		std::cout << (XQLog::logType_toString(log.logType) + " ").toLocal8Bit().data();
	}
	if (flags & XQLog::dataLineFeed)
	{
		std::cout << std::endl;
	}
	std::cout << QString(log.data).toLocal8Bit().data() << std::endl;
}

void LogLocalOut(const XQLogOut& log)
{
	auto& data = log.data;
	if(!QFile(log.localPath).exists())
	{//文件不存在
		auto dirPath = QFileInfo(log.localPath).dir().path();
		QDir().mkpath(dirPath);//递归创建目录
	}
	QWriteLocker lcok(&XQLog::m_lock);
	QFile file(log.localPath);
	if (!file.open(((log.SaveType == LogSaveType::append) ? QIODeviceBase::Append : QIODeviceBase::NotOpen) | QIODeviceBase::ReadWrite ))
		return;//打开失败
	if (file.size() != 0)
		file.write(",");
	file.write(data);
	file.close();
}

XQLogDate::XQLogDate(const QString& key,LogType type, const QVariantList& data, const QString& info, QDateTime time)
	:key(key),logType(type), data(data), time(time), info(info)
{

}

XQLogDate::XQLogDate(const QVariantMap& map)
{
	setData(map);
}

XQLogOut::XQLogOut(LogOutType outType, const XQLogSettings& Settings, const QByteArray& data, LogSaveType SaveType)
	:outType(outType),data(data),SaveType(SaveType)
{
	flags = Settings.flags;
	logType = Settings.logType;
	localPath = Settings.localPath;
	timeFormat = Settings.timeFormat;
}
